// See https://aka.ms/new-console-template for more information
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Text;
using ConsoleApp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static System.Console;

#region

//{

//    var pars = new string[] { @"C:\Users\curitis\Desktop\RoslynApi\ConsoleApp\GenericGeneratorExampleFile.cs" };
//    foreach (var argument in pars)
//    {
//        GenerateGenericTypes(argument, 4);
//    }
//    static void GenerateGenericTypes(string file, int count)
//    {
//        // 读取原始文件并创建泛型代码生成器。
//        var template = File.ReadAllText(file, Encoding.UTF8);
//        var generator = new GenericGenerator("curitis", template);

//        // 根据泛型个数生成目标文件路径和文件内容。
//        var format = GetIndexedFileNameFormat(file);
//        (string targetFileName, string targetFileContent)[] contents = Enumerable.Range(2, count - 1).Select(i =>
//            (string.Format(format, i), generator.Generate(i))
//        ).ToArray();

//        // 写入目标文件。
//        foreach (var writer in contents)
//        {
//            File.WriteAllText(writer.targetFileName, writer.targetFileContent);
//        }
//    }

//    static string GetIndexedFileNameFormat(string fileName)
//    {
//        var directory = Path.GetDirectoryName(fileName);
//        var name = Path.GetFileNameWithoutExtension(fileName);
//        if (name.EndsWith("1"))
//        {
//            name = name.Substring(0, name.Length - 1);
//        }

//        return Path.Combine(directory, name + "{0}.cs");
//    }
//}


#endregion

// *********************************************************************************************************************************

#region
//{
//    var filePath = @"C:\Users\curitis\Desktop\RoslynApi\ConsoleApp\GenericGeneratorV2.cs";
//    var originalText = File.ReadAllText(filePath);
//    var syntaxTree = CSharpSyntaxTree.ParseText(originalText);
//    var type = CompileType("GenericGeneratorV2", syntaxTree);
//    var transformer = Activator.CreateInstance(type);

//    var exampleFilePath = @"C:\Users\curitis\Desktop\RoslynApi\ConsoleApp\GenericGeneratorExampleFile.cs";
//    var newContent = (string)type.GetMethod("Transform").Invoke(transformer,
//        new object[] { File.ReadAllText(exampleFilePath), 2 });

//    Console.WriteLine(newContent);

//    Console.WriteLine("\r\n\r\n");
//    newContent = (string)type.GetMethod("Transform").Invoke(transformer,
//   new object[] { File.ReadAllText(exampleFilePath), 3 });

//    Console.WriteLine(newContent);


//    static Type CompileType(string originalClassName, SyntaxTree syntaxTree)
//    {

//        var assemblyName = $"{originalClassName}.g";

//        var compilation = CSharpCompilation.Create(assemblyName, new[] { syntaxTree },
//                    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
//                .AddReferences(
//                    // 这算是偷懒了吗？我把 .NET Core 运行时用到的那些引用都加入到引用了。
//                    // 加入引用是必要的，不然连 object 类型都是没有的，肯定编译不通过。
//                    AppDomain.CurrentDomain.GetAssemblies().Select(x => MetadataReference.CreateFromFile(x.Location)));

//        // 编译到内存流中。
//        using (var ms = new MemoryStream())
//        {
//            var result = compilation.Emit(ms);

//            if (result.Success)
//            {
//                ms.Seek(0, SeekOrigin.Begin);
//                var assembly = Assembly.Load(ms.ToArray());
//                return assembly.GetTypes().First(x => x.Name == originalClassName);
//            }
//            throw new CompilingException(result.Diagnostics);
//        }
//    }

//}
# endregion

// **********************************************************************************************************

#region
//SyntaxTree tree = CSharpSyntaxTree.ParseText("""
//public class MyClass
//{
//    public string MyMethod(int id)
//    {
//        return $"{id}";
//    }
//    public void Say()
//    {
//        var tmp = MyMethod();//worked
//        var tmp1 = MyMethod();//worked
//        var tmp2 = ""Hi "" + MyMethod();//worked
//        Console.WriteLine($"Say {MyMethod()}");//Not worked
//        Console.WriteLine("Hello " + MyMethod());//Not worked                    
//    }
//}
//""");

//var syntaxRoot = tree.GetCompilationUnitRoot();
//var visitor = new InvocationVisitor();
//visitor.Id = 10;
////var changedSyntaxTree = visitor.Visit(syntaxRoot).SyntaxTree;
//var node = visitor.Visit(syntaxRoot);
//Console.WriteLine(node.GetText());

#endregion

// **********************************************************************************************************

#region

//NameSyntax name = IdentifierName("System");
//WriteLine($"\tCreated the identifier {name}");

//name = QualifiedName(name, IdentifierName("Collections"));
//WriteLine(name.ToString());

//name = QualifiedName(name, IdentifierName("Generic"));
//WriteLine(name.ToString());


//const string code =
//@"using System;
//using System.Collections;
//using System.Linq;
//using System.Text;

//namespace HelloWorld
//{
//    class Program
//    {
//        static void Main(string[] args)
//        {
//            Console.WriteLine(""Hello, World!"");
//        }
//    }
//}";

//SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
//var root = (CompilationUnitSyntax)tree.GetRoot();
//var oldUsing = root.Usings[1];
//var newUsing = oldUsing.WithName(name);
//WriteLine(root.ToString());

//root = root.ReplaceNode(oldUsing, newUsing);
//WriteLine(root.ToString());
# endregion

// **********************************************************************************************************

#region

TypeInferenceTest1 variable1 = new TypeInferenceTest1(),
    variable2 = new TypeInferenceTest1();

TypeInferenceTest2 variable;

Compilation test = CreateTestCompilation();

foreach (SyntaxTree sourceTree in test.SyntaxTrees)
{
    var model = test.GetSemanticModel(sourceTree);

    var rewriter = new TypeInferenceRewriter(model);

    var newSource = rewriter.Visit(sourceTree.GetRoot());

    if (newSource != sourceTree.GetRoot())
    {
        File.WriteAllText(sourceTree.FilePath, newSource.ToFullString());
    }
}

Compilation CreateTestCompilation()
{
    string programPath = @"C:\Users\curitis\Desktop\RoslynApi\ConsoleApp\Program.cs";
    string programText = File.ReadAllText(programPath);
    var programTree =
                   CSharpSyntaxTree.ParseText(programText)
                                   .WithFilePath(programPath);

    string rewriterPath = @"C:\Users\curitis\Desktop\RoslynApi\ConsoleApp\TypeInferenceRewriter.cs";
    string rewriterText = File.ReadAllText(rewriterPath);
    var rewriterTree =
                   CSharpSyntaxTree.ParseText(rewriterText)
                                   .WithFilePath(rewriterPath);

    SyntaxTree[] sourceTrees = { programTree, rewriterTree };

    MetadataReference mscorlib =
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
    MetadataReference codeAnalysis =
            MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
    MetadataReference csharpCodeAnalysis =
            MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location);

    MetadataReference aaa =
          MetadataReference.CreateFromFile(typeof(File).Assembly.Location);

    MetadataReference[] references = { mscorlib, codeAnalysis, csharpCodeAnalysis,aaa };

    return CSharpCompilation.Create("TransformationCS",
        sourceTrees,
        references,
        new CSharpCompilationOptions(OutputKind.ConsoleApplication));
}


#endregion

//ClassDeclarationSyntax @class = SyntaxFactory.ClassDeclaration("MyClass");

//WriteLine(@class);

Console.ReadKey();



class InvocationVisitor : CSharpSyntaxRewriter
{
    public int Id { get; set; }

    public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node)
    {
        var invokName = node.Expression.ToString();
        if (invokName == "MyMethod")
        {
            var literalExpression = SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(Id));
            var argument = SyntaxFactory.Argument(literalExpression);
            var args = node.ArgumentList.AddArguments(argument);

            var es = (ExpressionSyntax)Visit(node.Expression);
            return node.Update(es, args);
        }

        return node;
    }
}

class CompilingException : Exception
{
    public CompilingException(ImmutableArray<Diagnostic> diagnostics)
    {

    }
}

class TypeInferenceTest1 { }

class TypeInferenceTest2 { }